﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Krile.Kernel.Bridges;
using System.Media;
using System.IO;

namespace Krile.Cores
{
    internal class NotificationManager: INotificationManager
    {
        private bool shutdown = false;

        public void Shutdown()
        {
            shutdown = true;
        }

        bool sleeping = false;
        static object waitLock = new object();
        List<Data.XStatus> wait = new List<Krile.Data.XStatus>();
        SoundPlayer player = null;
        bool containReplies = false;

        string overridedSoundPath = null;
        
        DateTime prevNotice = DateTime.MinValue;

        internal void BeginSleep()
        {
            sleeping = true;
        }

        internal void EndSleep()
        {
            sleeping = false;
            DoNotificate();
        }

        bool IsSleeping(bool replyStatus)
        {
            if (sleeping)
                return true;
            if (replyStatus && !Core.Config.Notification.IntervalApplyForAll)
                return false;
            if ((DateTime.Now - prevNotice).TotalSeconds < Core.Config.Notification.NotificationInterval)
                return true;
            else
                return false;
        }

        internal void SetNotificate(IEnumerable<Data.XStatus> statuses)
        {
            this.SetNotificate(statuses, null);
        }

        internal void SetNotificate(IEnumerable<Data.XStatus> statuses, string sound)
        {
            if (shutdown)
                return;
            if (!String.IsNullOrEmpty(sound) && File.Exists(sound))
                overridedSoundPath = sound;
            lock (waitLock)
            {
                foreach (var s in statuses)
                {
                    //自分発のステータスは通知しない
                    if (Core.Config.Notification.UnnotifyMyStatus &&
                       s.BaseStatus.ParentAgent.IsMasterId(s.BaseStatus.User.Id))
                        continue;
                    if (!wait.Contains(s) && s.Unread)
                    {
                        if (s.BaseStatus.ReplyData != null)
                        {
                            if ((s.BaseStatus.ReplyData.ReplyToUserId != null &&
                                s.BaseStatus.ParentAgent.IsMasterId(s.BaseStatus.ReplyData.ReplyToUserId)) ||
                                (s.BaseStatus.ReplyData.ReplyToId != null &&
                                s.BaseStatus.ParentAgent.Status.ServiceStatusIdExists(s.BaseStatus.ReplyData.ReplyToId) &&
                                Core.StatusManager.IdExists(s.BaseStatus.ParentAgent.Status.GetUidFromServiceStatusId(s.BaseStatus.ReplyData.ReplyToId)) &&
                                s.BaseStatus.ParentAgent.IsMasterId(Core.StatusManager.GetStatus(s.BaseStatus.ParentAgent.Status.GetUidFromServiceStatusId(s.BaseStatus.ReplyData.ReplyToId)).User.Id)))
                                containReplies = true;
                        }
                        wait.Add(s);
                    }
                }
            }
            DoNotificate();
        }

        internal void DoNotificate()
        {
            if (shutdown)
                return;
            if (wait == null)
                return;
            if (wait.Count == 0)
                return;
            lock (waitLock)
            {
                if (IsSleeping(containReplies))
                    return;
                if (containReplies) //is reply existed
                {
                    StringBuilder quote = new StringBuilder(40);

                    //reply enumerator
                    var swl = from s in wait
                              where s.BaseStatus.ReplyData != null && s.BaseStatus.ParentAgent.IsMasterId(s.BaseStatus.ReplyData.ReplyToUserId)
                              orderby s.BaseStatus.CreatedAt descending
                              select s;

                    //not-reply enumerator
                    var nwl = from s in wait
                              where !swl.Contains<Data.XStatus>(s)
                              orderby s.BaseStatus.CreatedAt descending
                              select s;

                    int gctor = 0;

                    // user_name:body
                    // or
                    // @user_id:body

                    //formatting to reply statuses
                    foreach (var s in swl)
                    {
                        if (String.IsNullOrEmpty(s.BaseStatus.User.Name))
                            quote.Append("@" + s.BaseStatus.User.Id);
                        else
                            quote.Append(s.BaseStatus.User.Name);

                        quote.Append(":");

                        if (s.BaseStatus.Text.Original.Length > Core.Config.Notification.BalloonStatusQuoteLength)
                            quote.AppendLine(s.BaseStatus.Text.Original.Substring(0, Core.Config.Notification.BalloonStatusQuoteLength));
                        else
                            quote.AppendLine(s.BaseStatus.Text.Original);

                        gctor++;
                        if (gctor >= Core.Config.Notification.BalloonStatusNum)
                            break;
                    }
                    if (gctor < Core.Config.Notification.BalloonStatusNum && nwl != null && nwl.Count<Data.XStatus>() > 0)
                    {
                        //formatting to normal statuses
                        foreach (var s in nwl)
                        {
                            if (String.IsNullOrEmpty(s.BaseStatus.User.Name))
                                quote.Append("@" + s.BaseStatus.User.Id);
                            else
                                quote.Append(s.BaseStatus.User.Name);

                            quote.Append(":");

                            if (s.BaseStatus.Text.Original.Length > Core.Config.Notification.BalloonStatusQuoteLength)
                                quote.AppendLine(s.BaseStatus.Text.Original.Substring(0, Core.Config.Notification.BalloonStatusQuoteLength));
                            else
                                quote.AppendLine(s.BaseStatus.Text.Original);

                            gctor++;
                            if (gctor >= Core.Config.Notification.BalloonStatusNum)
                                break;
                        }

                    }

                    if (Core.Config.Notification.FormAutoForeground)
                    {
                        Core.FormManager.MainForm.SafeOperate(() =>
                        {
                            Core.FormManager.MainForm.Show();
                            Core.FormManager.MainForm.Activate();
                        });
                    }
                    Core.FormManager.TrayForm.Notify.SetIcon(Core.FormManager.TrayForm.Notify.GetIconFile(Krile.Forms.TrayForm.Notifier.IconState.Reply), false);
                    if (!Core.Config.State.SilentMode)
                    {
                        if (!String.IsNullOrEmpty(overridedSoundPath) && File.Exists(overridedSoundPath))
                        {
                            if (!PlaySound(overridedSoundPath))
                                overridedSoundPath = null;
                        }
                        else
                            PlaySound(Core.FormManager.TrayForm.Notify.GetSoundFile(Krile.Forms.TrayForm.Notifier.SoundState.ReplyWav));
                        string det = null;
                        if (nwl != null && nwl.Count<Data.XStatus>() > 0)
                            det = String.Format(Lang.Tray.tip_NewStatus, wait.Count());
                        else
                            det = String.Format(Lang.Tray.tip_NewReply, swl.Count<Data.XStatus>(), nwl.Count<Data.XStatus>());
                        Core.FormManager.TrayForm.Notify.Popup(50000, det, quote.ToString(), System.Windows.Forms.ToolTipIcon.Info);
                    }
                    overridedSoundPath = null;
                }
                else //receive new
                {
                    StringBuilder quote = new StringBuilder(40);
                    var wl = from s in wait
                             orderby s.BaseStatus.CreatedAt descending
                             select s;
                    int gctor = 0;

                    // user_name:body
                    // or
                    // @user_id:body
                    foreach (var s in wl)
                    {
                        if (String.IsNullOrEmpty(s.BaseStatus.User.Name))
                            quote.Append("@" + s.BaseStatus.User.Id);
                        else
                            quote.Append(s.BaseStatus.User.Name);

                        quote.Append(":");

                        if (s.BaseStatus.Text.Original.Length > Core.Config.Notification.BalloonStatusQuoteLength)
                            quote.AppendLine(s.BaseStatus.Text.Original.Substring(0, Core.Config.Notification.BalloonStatusQuoteLength));
                        else
                            quote.AppendLine(s.BaseStatus.Text.Original);

                        gctor++;
                        if (gctor >= Core.Config.Notification.BalloonStatusNum)
                            break;
                    }
                    var det = String.Format(Lang.Tray.tip_NewStatus, wait.Count);
                    if (Core.Config.Notification.FormAutoForeground)
                    {
                        Core.FormManager.MainForm.SafeOperate(() =>
                        {
                            Core.FormManager.MainForm.Show();
                            Core.FormManager.MainForm.Activate();
                        });
                    }
                    Core.FormManager.TrayForm.Notify.SetIcon(Core.FormManager.TrayForm.Notify.GetIconFile(Krile.Forms.TrayForm.Notifier.IconState.RecvNew), false);
                    if (!Core.Config.State.SilentMode)
                    {
                        if (!String.IsNullOrEmpty(overridedSoundPath) && File.Exists(overridedSoundPath))
                            PlaySound(overridedSoundPath);
                        else
                            PlaySound(Core.FormManager.TrayForm.Notify.GetSoundFile(Krile.Forms.TrayForm.Notifier.SoundState.RecvNewWav));
                        Core.FormManager.TrayForm.Notify.Popup(50000, det, quote.ToString(), System.Windows.Forms.ToolTipIcon.Info);
                    }
                    overridedSoundPath = null;
                }
                wait.Clear();
                containReplies = false;
                prevNotice = DateTime.Now;
            }
        }

        private bool PlaySound(string file)
        {
            if (player != null)
            {
                player.Dispose();
                player = null;
            }
            if (!Core.Config.Notification.PlaySound) return true;
            player = new SoundPlayer(Path.Combine(Define.AppPath, Path.Combine(Define.ResourceDir, file)));
            try
            {
                player.Play();
            }
            catch (InvalidOperationException e)
            {
                Subsystem.Debugger.AddReport("Format error: this file can not play. :" + e.Message);
                return false;
            }
            catch (Exception e)
            {
                Subsystem.Debugger.AddReport(e);
            }
            return true;
        }

        internal void NotifyNewReportAvaliable()
        {
            Core.FormManager.TrayForm.Notify.SetIcon(Core.FormManager.TrayForm.Notify.GetIconFile(Krile.Forms.TrayForm.Notifier.IconState.Error), false);
            if (!Core.Config.State.SilentMode)
            {
                PlaySound(Core.FormManager.TrayForm.Notify.GetSoundFile(Krile.Forms.TrayForm.Notifier.SoundState.NotifyWav));
                Core.FormManager.TrayForm.Notify.Popup(50000, Lang.Tray.tip_NewReport, null, System.Windows.Forms.ToolTipIcon.Error);
            }
        }

        public void NotifyInfo(string body, string title)
        {
            Core.FormManager.TrayForm.Notify.SetIcon(Core.FormManager.TrayForm.Notify.GetIconFile(Krile.Forms.TrayForm.Notifier.IconState.Notice), false);
            if (!Core.Config.State.SilentMode)
            {
                PlaySound(Core.FormManager.TrayForm.Notify.GetSoundFile(Krile.Forms.TrayForm.Notifier.SoundState.NotifyWav));
                Core.FormManager.TrayForm.Notify.Popup(50000, title, body, System.Windows.Forms.ToolTipIcon.Info);
            }
        }
    }
}
